package rok3.projekt.zespver3.server;

import static rok3.projekt.zespver3.client.Settings._port_tcp;
import static rok3.projekt.zespver3.client.Settings._port_udp;

import java.io.IOException;
import java.util.Random;
import java.util.Scanner;

import rok3.projekt.zespver3.network.Network;
import rok3.projekt.zespver3.network.packet.CharacterState__;
import rok3.projekt.zespver3.network.packet.Event__;
import rok3.projekt.zespver3.network.packet.PrimitiveEvent__;
import rok3.projekt.zespver3.network.packet.__AllChanges;
import rok3.projekt.zespver3.network.packet.__CharacterState;
import rok3.projekt.zespver3.network.packet.__Event;
import rok3.projekt.zespver3.network.packet.__PrimitiveEvent;
import rok3.projekt.zespver3.network.packet.__PrimitiveSignal__;
import rok3.projekt.zespver3.network.packet.__StartGameInfo;
import rok3.projekt.zespver3.network.packet.__YourId;

import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;

public class MainServer {

	public static class MyConnection extends Connection {
		public boolean ready = false;
		public short unique_id;
	}

	public static Array<MyConnection> users = new Array<MyConnection>(false, 16);
	public static Random random = new Random();
	public static volatile boolean server_running = true;

	/* tmp */
	public static volatile boolean game_starting = false;
	public static volatile boolean game_started = false;

	public static void main(String[] args) {
		final Array<__CharacterState> allCharStates = new Array<>(true, 32,__CharacterState.class);
		final Array<__Event> allEvents = new Array<>(true, 32,__Event.class);
		
		final __AllChanges __allChanges = new __AllChanges();
		final __StartGameInfo __tmp_startGameInfo = new __StartGameInfo();
		
		
		final Server server = new Server() {
			@Override
			protected Connection newConnection() {
				return new MyConnection();
			}
		};

		Network.register(server);

		server.addListener(new Listener() {
			float last_timestamp = 0;

			@Override
			public void received(Connection arg0, Object arg1) {
				MyConnection cc = (MyConnection) arg0;
				/*
				 * Primitive signals 
				 */
				if (arg1 instanceof __PrimitiveSignal__) {
					__PrimitiveSignal__ p__ = (__PrimitiveSignal__) arg1;
					switch (p__) {
						/*
						 * Primitive signals - ready for start signal
						 */
						case ready__: {
							cc.ready = true;
							System.out.println(cc.unique_id + " is reayd");
							break;
						}
						/*
						 * Primitive signals - start signal
						 */
						case start__: {
							boolean k = true;
							game_starting = true;
							synchronized (users) {
								for (MyConnection mc : users) {
									if (!mc.ready)
										k = false;
								}
							}
							if (k) {
								System.out.println("OK- can start");
								__tmp_startGameInfo.player_ids = new short[users.size];
								for (short i = 0; i < users.size; i++) {
									__tmp_startGameInfo.player_ids[i] = users
											.get(i).unique_id;
								}
								server.sendToAllTCP(__tmp_startGameInfo);
								System.out.println("Server> sent start game info");
								game_started = true;
							} else {
								game_starting = false;
								System.out.println("NO- cant start");
							}
							break;
						}
						
						
						default: {
							System.out.println("Recived unknown primitive signal");
						}
					}
				}
				

				/*
				 * Character state : position, rotation, local timestamp, source's id
				 */
				if (arg1 instanceof CharacterState__) {
					CharacterState__ cs__ = (CharacterState__) arg1;
					float delta = cs__.local_timestamp - last_timestamp;
					__CharacterState __state = new __CharacterState();
					__state.x = cs__.x;
					__state.y = cs__.y;
					__state.local_timestamp = cs__.local_timestamp;
					__state.rotation = cs__.rotation;
					__state.id = cs__.id;
					/* need __state.hp */
					synchronized (allCharStates) {
						boolean k=true;
						for (short i=0 ; i< allCharStates.size; i++) {
							if ( allCharStates.items[i].id == __state.id ) {
								allCharStates.items[i] = __state;
								k=false;
								break;
							}
						}
						if(k) allCharStates.add(__state);
					}
				}
				
				
				/*
				 * Events
				 */
				if (arg1 instanceof Event__[] ) {
					Event__[] events = (Event__[]) arg1;
					for ( Event__ e : events) {
						if ( e instanceof PrimitiveEvent__ ) {
							PrimitiveEvent__ e2 = (PrimitiveEvent__) e;
							switch (e2.type ) {
								/*
								 * Primitive event - shoot 
								 */
								case shoot__:{
									__PrimitiveEvent se = new __PrimitiveEvent();
									se.source_id = cc.unique_id;
									synchronized (allEvents) {
										allEvents.add(se);
									}
									break;
								}
							}
						}
					}
				}
			}

			@Override
			public void connected(Connection arg0) {
				System.out.println("Server> connected from #" + arg0.getID()
						+ "#" + arg0.getRemoteAddressTCP());
				if (game_started) {
					System.out.println("game_started");
					return;
				}
				if (game_starting) {
					System.out.println("game_starting");
					return;
				}
				short id;
				do {
					id = (short) (random.nextInt(Short.MAX_VALUE
							- Short.MIN_VALUE + 1) - Short.MAX_VALUE);
					System.out.printf("generated id : %d\n", id);
				} while (!isIdValid(id));
				((MyConnection) arg0).unique_id = id;

				synchronized (users) {
					users.add((MyConnection) arg0);
				}
				__YourId yourid = new __YourId();
				yourid.id = id;
				server.sendToTCP(arg0.getID(), yourid);
				System.out.println("Server> sent id");
			}

			boolean isIdValid(short id) {
				for (MyConnection m : users) {
					if (m.unique_id == id)
						return false;
				}
				return true;
			}

			@Override
			public void disconnected(Connection arg0) {
				System.out.println("Server> disconnected from #" + arg0.getID());
				users.removeValue(((MyConnection) arg0), true);
			}
		});
		try {
			server.bind(_port_tcp, _port_udp);
		} catch (NumberFormatException | IOException e) {
			System.out.println("Error: server cant bind");
			System.out.println(-1);
		}

		new Thread(server).start();

		new Thread() {
			public void run() {
				Scanner in = new Scanner(System.in);
				char i = 'a';
				do {
					if (in.hasNext())
						i = in.next().charAt(0);
					if (i == 'c') {
						System.out.printf("Connections :%d\n",
								server.getConnections().length);
					}
					if (i == 'b') {
						System.out.printf("Connections :%d\n", users.size);
					}
					if (i == 'r') {
						game_started = false;
						game_starting = false;
					}
				} while (i != 'q');
				server.stop();
				server_running = false;
				in.close();
			};
		}.start();

		new Thread() {
			final int interval = 100;

			public void run() {
				while (!game_started)
					;
				long deltatime = 0;
				long lastStartTime = 0;
				while (server_running) {
					lastStartTime = System.currentTimeMillis();
					if (deltatime < interval) {
						try {
							Thread.sleep(interval - deltatime);
						} catch (Exception e) {
							System.err.println("TSEngine :: run :: " + e);
						}
					} else {
						if (allCharStates.size != 0) {
							synchronized (allCharStates) {
								__allChanges.states = allCharStates.toArray();
							}
							synchronized (allEvents) {
								__allChanges.events = allEvents.toArray();
							}
							
							server.sendToAllUDP(__allChanges);
							synchronized (allCharStates) {
								allCharStates.clear();
							}
							synchronized (allEvents) {
								allEvents.clear();
							}
						}

						deltatime -= interval;
					}

					deltatime += System.currentTimeMillis() - lastStartTime;
				}
			};
		}.start();
	}
}
